﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using Framework.ManejoExcepciones;

namespace Framework.AccesoDatos
{
	public class SQL
	{
		#region Enumeradores
		
		public enum TipoSentenciaSQL
		{
			SELECT_DS,
			SELECT_DV,
			SELECT_DR,
			SELECT_UD,
			MANIPULATION
		}

		#endregion

		#region Campos

		private uint _nRegistrosAfectados;
		private uint _nSegundosEjecucion;
		private SqlConnection _oConexion;
		private SqlDataReader _oDR;
		private DataSet _oDS;
		private DataView _oDV;
		private List<Relacion> _oRelaciones;
		private List<SentenciaSQL> _oSentenciasSQL;
		private string _sCadenaConexion;
		private string _sUnicoDato;

		#endregion

		#region Metodos

		public void AbrirConexion()
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "AbrirConexion");
			try
			{
				if (EstatusConexion != ConnectionState.Open)
				{
					Conexion.ConnectionString = CadenaConexion;
					Conexion.Open();
				}
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public void AgregarRelacion(string psAlias, string psAliasTablaPadre, string psCampoTablaPadre, string psAliasTablaHija, string psCampoTablaHija)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "AgregarRelacion");
			try
			{
				Relacion item = new Relacion(psAlias, psAliasTablaPadre, psCampoTablaPadre, psAliasTablaHija, psCampoTablaHija);
				Relaciones.Add(item);
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public void AgregarSentenciaSQL(string psSentenciaSQL, TipoSentenciaSQL peTipoSentenciaSQL)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "AgregarSentenciaSQL");
			try
			{
				SentenciaSQL item = new SentenciaSQL(psSentenciaSQL, "", peTipoSentenciaSQL);
				SentenciasSQL.Add(item);
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message);
			}
		}

		public void AgregarSentenciaSQL(string psSentenciaSQL, string psAlias, TipoSentenciaSQL peTipoSentenciaSQL)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "AgregarSentenciaSQL");
			try
			{
				SentenciaSQL item = new SentenciaSQL(psSentenciaSQL, psAlias, peTipoSentenciaSQL);
				SentenciasSQL.Add(item);
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "Alias=" + psAlias);
			}
		}

		public void CambiarBD(string psNuevaBD)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "CambiarBD");
			try
			{
				Conexion.ChangeDatabase(psNuevaBD);
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public void CerrarConexion()
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "CerrarConexion");
			try
			{
				if (EstatusConexion != ConnectionState.Closed)
				{
					if ((DR != null) && !DR.IsClosed)
					{
						DR.Close();
					}
					Conexion.Close();
				}
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public void EjecutarComando(SqlCommand poComando, TipoSentenciaSQL peTipoSentencia)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "EjecutarComando");
			try
			{
				if (EstatusConexion != ConnectionState.Open)
				{
					AbrirConexion();
				}
				poComando.Connection = Conexion;
				poComando.CommandTimeout = (int)SegundosEjecucion;
				switch (peTipoSentencia)
				{
					case TipoSentenciaSQL.SELECT_DS:
						new SqlDataAdapter(poComando).Fill(DS);
						return;

					case TipoSentenciaSQL.SELECT_DV:
						new SqlDataAdapter(poComando).Fill(DS);
						_oDV = DS.Tables[0].DefaultView;
						return;

					case TipoSentenciaSQL.SELECT_DR:
						_oDR = poComando.ExecuteReader();
						return;

					case TipoSentenciaSQL.SELECT_UD:
						_sUnicoDato = poComando.ExecuteScalar().ToString();
						return;

					case TipoSentenciaSQL.MANIPULATION:
						_nRegistrosAfectados = (uint)poComando.ExecuteNonQuery();
						return;
				}
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public void EjecutarSentencias()
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "EjecutarSentencias");
			try
			{
				if (EstatusConexion != ConnectionState.Open)
				{
					AbrirConexion();
				}
				SqlCommand selectCommand = new SqlCommand();
				bool flag = false;
				selectCommand.Connection = Conexion;
				selectCommand.CommandTimeout = (int)SegundosEjecucion;
				try
				{
					foreach (SentenciaSQL asql in SentenciasSQL)
					{
						selectCommand.CommandText = asql.Sentencia;
						switch (asql.TipoSentenciaSQL)
						{
							case TipoSentenciaSQL.SELECT_DS:
								{
									new SqlDataAdapter(selectCommand).Fill(DS, asql.Alias);
									flag = true;
									continue;
								}
							case TipoSentenciaSQL.SELECT_DV:
								{
									new SqlDataAdapter(selectCommand).Fill(DS, asql.Alias);
									_oDV = DS.Tables[0].DefaultView;
									flag = true;
									continue;
								}
							case TipoSentenciaSQL.SELECT_DR:
								{
									_oDR = selectCommand.ExecuteReader();
									continue;
								}
							case TipoSentenciaSQL.SELECT_UD:
								{
									_sUnicoDato = selectCommand.ExecuteScalar().ToString();
									continue;
								}
							case TipoSentenciaSQL.MANIPULATION:
								{
									_nRegistrosAfectados = (uint)selectCommand.ExecuteNonQuery();
									continue;
								}
						}
					}
					try
					{
						if (flag && (Relaciones.Count > 0))
						{
							foreach (Relacion relacion in Relaciones)
							{
								DataRelation relation = new DataRelation(relacion.Alias, DS.Tables[relacion.AliasTablaPadre].Columns[relacion.CampoTablaPadre], DS.Tables[relacion.AliasTablaHija].Columns[relacion.CampoTablaHija]);
								DS.Relations.Add(relation);
							}
						}
					}
					catch (Exception exception)
					{
						excepciones.LanzarExcepcion("Crear relaciones en el DataSet", exception.Message, "");
					}
				}
				catch (Exception exception2)
				{
					excepciones.LanzarExcepcion("Ejecutar sentencias SQL", exception2.Message, "");
				}
			}
			catch (Exception exception3)
			{
				excepciones.LanzarExcepcion("Catch", exception3.Message, "");
			}
		}

		public void RestablecerDatosEstadisticos()
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "RestablecerDatosEstadisticos");
			try
			{
				Conexion.ResetStatistics();
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		#endregion

		#region Propiedades

		public string BaseDatos { get { return Conexion.Database; } }

		public string CadenaConexion { get { return _sCadenaConexion; } set { _sCadenaConexion = value; } }

		public static string Comilla { get { return "\x00ac"; } }

		private SqlConnection Conexion { get { return _oConexion; } }

		public IDictionary DatosEstadisticos { get { return Conexion.RetrieveStatistics(); } }

		public SqlDataReader DR { get { return _oDR; } }

		public DataSet DS { get { return _oDS; } }

		public DataView DV { get { return _oDV; } }

		public ConnectionState EstatusConexion { get { return Conexion.State; } }

		public bool HabilitarEstadisticas { get { return Conexion.StatisticsEnabled; } set { Conexion.StatisticsEnabled = value; } }

		public uint RegistrosAfectados { get { return _nRegistrosAfectados; } }

		private List<Relacion> Relaciones { get { return _oRelaciones; } }

		public uint SegundosEjecucion
		{
			get
			{
				return _nSegundosEjecucion;
			}
			set
			{
				_nSegundosEjecucion = value;
			}
		}

		private List<SentenciaSQL> SentenciasSQL
		{
			get
			{
				return _oSentenciasSQL;
			}
		}

		public string Servidor
		{
			get
			{
				return Conexion.DataSource;
			}
		}

		public string UnicoDato
		{
			get
			{
				if (_sUnicoDato == null)
				{
					_sUnicoDato = "";
				}
				return _sUnicoDato;
			}
		}

		public string VersionServidor
		{
			get
			{
				return Conexion.ServerVersion;
			}
		}

		#endregion

		#region Constructores

		private void InicializarCampos()
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "InicializarCampos");

			try
			{
				_sCadenaConexion = "";
				_oSentenciasSQL = new List<SentenciaSQL>();
				_nSegundosEjecucion = 60;
				_oConexion = new SqlConnection();
				_oDS = new DataSet();
				_sUnicoDato = "";
				_nRegistrosAfectados = 0;
				_oRelaciones = new List<Relacion>();
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message);
			}
		}

		public SQL()
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "Constructor por default");
			try
			{
				InicializarCampos();
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public SQL(string psKeyConfig)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "Constructor por llave en el archivo de configuraci\x00f3n");
			try
			{
				InicializarCampos();
				CadenaConexion = ConfigurationManager.ConnectionStrings[psKeyConfig].ToString();
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		public SQL(string psServidor, string psBD, string psUsuario, string psContrasenia)
		{
			ManejadorExcepciones excepciones = new ManejadorExcepciones(this, "Constructor por par\x00e1metros");
			try
			{
				InicializarCampos();
				CadenaConexion = "Data Source=" + psServidor + ";Initial Catalog=" + psBD + ";User Id=" + psUsuario + "; Pwd=" + psContrasenia;
			}
			catch (Exception exception)
			{
				excepciones.LanzarExcepcion("Catch", exception.Message, "");
			}
		}

		#endregion 
	}
}